
package fibril

import "unsafe"
import "syscall"

type SwitchType int
type ID syscall.FibrilID

const (
	PREEMPT = SwitchType(syscall.FIBRIL_PREEMPT)
	TO_MANAGER = SwitchType(syscall.FIBRIL_TO_MANAGER)
	FROM_MANAGER = SwitchType(syscall.FIBRIL_FROM_MANAGER)
	FROM_DEAD = SwitchType(syscall.FIBRIL_FROM_DEAD)
)

type FibrilMain interface {
	RunFibril()
}

type FibrilMainFunc func()

func (f FibrilMainFunc) RunFibril() {
	f()
}

//extern fibril_create
func fibril_create(func(unsafe.Pointer)int, unsafe.Pointer) ID

//extern runtime_fibril_setup
func fibril_setup()

//extern runtime_fibril_teardown
func fibril_teardown()

func fibrilMain(p unsafe.Pointer) int {
	f := *(*FibrilMain)(p)
	fibril_setup()
	f.RunFibril()
	// FIXME: Make this panic-proof.
	fibril_teardown()
	return 0
}

// Registers the fibril as a goroutine, so that it can execute Go code.
// That means "fibril.AddReady(fibril.Create(func))" means the same as "go func()".
func Create(f FibrilMain) ID {
	// FIXME: Must hold reference to &f in Go code,
	//        otherwise GC may deallocate it before the fibril runs.
	return fibril_create(fibrilMain, unsafe.Pointer(&f))
}

//extern fibril_destroy
func fibril_destroy(fid ID)

func (fid ID) Destroy() {
	fibril_destroy(fid)
}

//extern fibril_switch
func fibril_switch(t int) int

func Switch(t SwitchType) bool {
	return fibril_switch(int(t)) != 0
}

//extern fibril_add_ready
func AddReady(fid ID)

//extern fibril_add_manager
func AddManager(fid ID)

//extern fibril_remove_manager
func RemoveManager()

//extern fibril_get_id
func GetID() ID

//extern fibril_inc_sercount
func IncSercount()

//extern fibril_dec_sercount
func DecSercount()

//extern fibril_get_sercount
func Sercount() int

func Yield() bool {
	return Switch(PREEMPT)
}

